#include "OString.h"
#include "Defines.h"

#include <boost/algorithm/string.hpp>


namespace Y{

int OString::OS = -1;

OString::OString()
{}

OString::OString( char c ):
	str()
{str.append(1,c);}

OString::OString( const char *c ):
	str(c)
{}

OString::OString( const std::string& s ):
	str(s)
{}

OString::~OString(){}

//
//#define OSTRING_COMPARISON_CODE(...) \
//		bool operator __VA_ARGS__ (const OString& s0, const OString& s1){ return (s0.std_string() __VA_ARGS__ s1.std_string()); }\
//		bool operator __VA_ARGS__ (const OString& s0, const std::string& s1){ return (s0.std_string() __VA_ARGS__ s1); }\
//		bool operator __VA_ARGS__ (const std::string& s0, const OString& s1){ return (s0 __VA_ARGS__ s1.std_string()); }\
//
//OSTRING_COMPARISON_CODE(==);
//OSTRING_COMPARISON_CODE(!=);
//OSTRING_COMPARISON_CODE(>);
//OSTRING_COMPARISON_CODE(<);
//OSTRING_COMPARISON_CODE(>=);
//OSTRING_COMPARISON_CODE(<=);

//bool operator== (const OString& s0,const char *c){ return (strcmp(s0.data(),c)); }
//bool operator== (const char *c,const OString& s0){ return (strcmp(s0.data(),c)); }

int OString::size() const{ return str.size(); }
int OString::length() const{ return str.length(); }


OString& OString::append ( const std::string& s ){
	str.append(s);
	return *this;
}

OString& OString::append ( const char *c ){
	str.append(c);
	return *this;
}

OString& OString::append ( const OString& os ){
	str.append( os.str );
	return *this;
}

OString& OString::append ( int i ){ std::ostringstream tmp; tmp << i; str.append( tmp.str() );return *this; }

OString OString::substring ( int p0, int p1 ){
	if ( p1 == -1 ){ p1 = str.length() - 1; }
	if ( p0 < 0 || p1 < 0 ){ return OString(); }
	else if ( p0 > p1 ){ int tmp = p0; p0 = p1; p1 = tmp; }
	if ( p1 >= length() ){ std::cout << "\n" << "Substring exceeds length of string"; return OString(); }
	
	return str.substr( p0, (p1 - p0) + 1 );
}

OString OString::substring_l ( int p0, int l){
	if ( l == -1 ) { return str.substr(p0); }
	return str.substr(p0,l);
}

OString OString::substring_c ( int p0, char c ){			//Substring, character, i.data.e. Substring between index and character
	int p1 = str.find(c,p0);
	if ( p1 == std::string::npos ){ return OString(); }
	else if ( p1 < p0 ) { return OString(); }
	return str.substr( p0, (p1 - p0) + 1 );
}

OString OString::substring_bc ( int p0, char c ){		//Substring, before character, i.data.e. Substring between index and one before character
	int p1 = str.find(c,p0) - 1;
	if ( p1 == std::string::npos ){ return OString(); }
	else if ( p1 < p0 ) { return OString(); }
	return str.substr( p0, (p1 - p0) + 1 );	
}


int OString::hash() const{
   int b    = 378551; //Ramdom Ranges
   int a    = 63689;
   int tmp = 0;

   for(int i = 0; i < length(); i++)
   {
      tmp = tmp * a + at(i);
      a	= a * b;
   }

   return (tmp & 0x7FFFFFFF); //Returns the hashed string as an int
}



void OString::toOSPath (){
	if ( OS == WINDOWS ){
		OString nstr;
		if ( str[0] == '/' ){
			nstr.append("C:\\");
		}
		for (int i = 0; i < size(); ++i){
			if ( str[i] == '/' ){
				nstr.append("\\");
			}
			else{
				nstr.append(substring_l(i,1));
			}
		}
		
		(*this) = nstr;
	}
}




OString OString::cullBackFrom ( const char *c, int length, int searchStart ){
	if ( searchStart == -1 ){ searchStart = str.length() - 1; }
	
	int loc = str.find(c,searchStart);
	if ( loc == std::string::npos ){ return *this; }
	loc += strlen(c) - 1;
	
	if ( length == -1 ){ length = loc + 1; }
	else if ( length > loc + 1 ){ length = loc + 1; }
	
	std::string r_str = str;
	r_str.erase( loc - (length - 1), length );
	
	return OString(r_str);
}

OString OString::cullOnFrom ( const char *c, int length, int searchStart ){
	if ( searchStart == -1 ){ searchStart = str.length() - 1; }
	
	int loc = str.find(c);
	if ( loc == std::string::npos ){ return *this; }
	
	if ( length == -1 ){ length = str.length() - loc; }
	else if ( length > str.length() - loc ){ length = str.length() - loc; }
	
	std::string r_str = str;
	r_str.erase( loc, length );
	
	return OString(r_str);
}

OString OString::rcullBackFrom ( const char *c, int length, int searchStart ){
	if ( searchStart == -1 ){ searchStart = str.length() - 1; }
	
	int loc = str.rfind(c,searchStart);
	if ( loc == std::string::npos ){ return *this; }
	loc += strlen(c) - 1;
	
	if ( length == -1 ){ length = loc + 1; }
	else if ( length > loc + 1 ){ length = loc + 1; }
	
	std::string r_str = str;
	r_str.erase( loc - (length - 1), length );
	
	return OString(r_str);
}

OString OString::rcullOnFrom ( const char *c, int length, int searchStart ){
	if ( searchStart == -1 ){ searchStart = str.length() - 1; }
	
	int loc = str.rfind(c);
	if ( loc == std::string::npos ){ return *this; }
	
	if ( length == -1 ){ length = str.length() - loc; }
	else if ( length > str.length() - loc ){ length = str.length() - loc; }
	
	std::string r_str = str;
	r_str.erase( loc, length );
	
	return OString(r_str);
}



OString OString::cullBefore ( const char *c, int length, int searchStart ){
	if ( searchStart == -1 ){ searchStart = str.length() - 1; }
	
	int loc = str.find(c,searchStart) - 1;
	if ( loc == std::string::npos ){ return *this; }
	
	if ( length == -1 ){ length = loc + 1; }
	else if ( length > loc + 1 ){ length = loc + 1; }
	
	std::string r_str = str;
	r_str.erase( loc - (length - 1), length );
	
	return OString(r_str);
}

OString OString::cullAfter ( const char *c, int length, int searchStart ){
	if ( searchStart == -1 ){ searchStart = str.length() - 1; }
	
	int loc = str.find(c);
	if ( loc == std::string::npos ){ return *this; }
	loc += strlen(c);
	
	if ( length == -1 ){ length = str.length() - loc; }
	else if ( length > str.length() - loc ){ length = str.length() - loc; }
	
	std::string r_str = str;
	r_str.erase( loc, length );
	
	return OString(r_str);
}

OString OString::rcullBefore ( const char *c, int length, int searchStart ){
	if ( searchStart == -1 ){ searchStart = str.length() - 1; }
	
	int loc = str.rfind(c,searchStart) - 1;
	if ( loc == std::string::npos ){ return *this; }
	
	if ( length == -1 ){ length = loc + 1; }
	else if ( length > loc + 1 ){ length = loc + 1; }
	
	std::string r_str = str;
	r_str.erase( loc - (length - 1), length );
	
	return OString(r_str);
}

OString OString::rcullAfter ( const char *c, int length, int searchStart ){
	if ( searchStart == -1 ){ searchStart = str.length() - 1; }
	
	int loc = str.rfind(c);
	if ( loc == std::string::npos ){ return *this; }
	loc += strlen(c);
	
	if ( length == -1 ){ length = str.length() - loc; }
	else if ( length > str.length() - loc ){ length = str.length() - loc; }
	
	std::string r_str = str;
	r_str.erase( loc, length );
	
	return OString(r_str);
}



OString& OString::remove ( int p0, int p1 ){
	str.erase( p0 , (p1 - p0) + 1 );
	return *this;
}

OString& OString::remove_c ( int p0, char c ){
	int p1 = str.find(c,p0);
	if ( p1 == std::string::npos ){ return *this; }
	else if ( p1 < p0 ) { return *this; }
	str.erase( p0, (p1 - p0) + 1 );
	return *this;
}

OString& OString::chop ( int n ){
	str.erase( length() - n, n );
	return *this;
}

OString& OString::replace ( int p0, int p1, const char *c ){
	str.replace( p0,p1 - p0 + 1,c );
	return *this;
}

OString& OString::replace ( const char* s0, const char * s1 ){
	int lastp = 0;
	int l0 = strlen(s0);
	int l1 = strlen(s1);
	while ( true ){
		lastp = str.find(s0,lastp);
		if ( lastp == std::string::npos ){ break; }
		
		str.replace(lastp,l0,s1,l1);
	}
	return *this;
}

OString& OString::replace_all_with ( const char *tokens, char replacer ){
	int sl = strlen(tokens);
	
	for ( int i = 0; i < size(); ++i ){
		for ( int j = 0; j < sl; ++j ){
			if ( at(i) == tokens[j] ){ at(i) = replacer;break; }
		}
	}
	
	return *this;
}

OString& OString::trim (){
	boost::trim( str );
	return *this;
}

OString& OString::to_lower (){
	boost::to_lower(str);
	return *this;
}
OString& OString::to_upper (){
	boost::to_upper(str);
	return *this;
}
OString OString::to_lower_cpy () const{
	OString tmp(str);
	tmp.to_lower();
	return tmp;
}
OString OString::to_upper_cpy () const{
	OString tmp(str);
	tmp.to_upper();
	return tmp;
}

int OString::find ( const char *c, int p0 ){
	return str.find(c,p0);
}

int OString::rfind ( const char *c, int p0 ){
	if ( p0 == -1 ) { p0 = str.length() - 1; }
	return str.rfind(c,p0);
}

int OString::find_first_of ( const char *c, int p0 ){
	return str.find_first_of(c,p0);
}


bool OString::lenientEquals (const char *c) const{
	int index0 = 0;
	int index1 = 0;
	int size0 = size();
	int size1 = strlen(c);
	for (;index0 < size0 && index1 < size1;){
		char c0 = at(index0);
		if ( c0 == ' ' || c0 == '\t' || c0 == '\n' ){ ++index0;continue; }
		char c1 = c[index1];
		if ( c1 == ' ' || c1 == '\t' || c1 == '\n' ){ ++index1;continue; }
		
		if ( tolower(c0) != tolower(c1) ){ return false; }
		
		++index0;
		++index1;
	}
	
	for (;index0 < size0;++index0){
		char c0 = at(index0);
		if ( c0 == ' ' || c0 == '\t' || c0 == '\n' ){}
		else{ return false; }
	}
	for (;index1 < size1;++index1){
		char c0 = c[index1];
		if ( c0 == ' ' || c0 == '\t' || c0 == '\n' ){}
		else{ return false; }
	}
	
	return true;
}

/*
	======================================================================================
	This is the area for the OPString -- OdinsLib Parseable String -- functions, nuff said
	======================================================================================
																								*/




OPString::OPString():
	OString(),
	placemark(0),
	delimiterIndex(0)
{}

OPString::OPString( const char *c ):
	OString(c),
	placemark(0),
	delimiterIndex(0)
{}

OPString::OPString( const std::string& s ):
	OString(s),
	placemark(0),
	delimiterIndex(0)
{}

OPString::OPString( const OString& os ):
		OString( os.data() ),
		placemark(0),
		delimiterIndex(0)
{}

OPString& OPString::operator= ( const OString& os ){
	str = os.str;
	return *this;
}

OPString& OPString::operator= ( const char *c ){
	str = c;
	return *this;
}

void OPString::setDelimiters ( const char *c ){
	delimiters.clear();
	for ( int i = 0; i < strlen(c); ++i ){
		delimiters.push_back( OString(c[i]) );
	}
}

void OPString::addDelimiters ( const char *c ){
	for ( int i = 0; i < strlen(c); ++i ){
		delimiters.push_back( OString(c[i]) );
	}
}

void OPString::addStringDelimiter ( const char *c ){
	delimiters.push_back( OString(c) );
}

void OPString::clearDelimiters (){
	delimiters.clear();
}
	
void OPString::parse (bool das){
	
	std::map < int, int > delimPosMap;
	int strl = str.length();
	bool occupied [ strl ];
	for ( int i = 0; i < strl; ++i ){
		occupied[i] = false;
	}
	
	for ( int i = 0; i < delimiters.size(); ++i ){
		int l = delimiters[i].length();
		int mark = 0;
		while ( true ){
			int f = str.find( delimiters[i].std_string(),mark );
			if ( f == std::string::npos ){ break; }
			
			mark = f + l;
			
			bool test = true;
			for ( int j = f; j < f + l; ++j ){
				if ( occupied[j] ){ test = false;break; }
			}
			if ( !test ){ continue; }
			
			for ( int j = f; j < f + l; ++j ){
				occupied[j] = true;
			}
			
			delimPosMap[f] = i;
		}
	}
	
	if ( delimPosMap.size() > 0 ){
		typeof( delimPosMap.end() ) it = delimPosMap.end();
		--it;
		if ( it->first != strl - 1 ){ delimPosMap[strl] = 0; }
	}
	
	
	int n = delimPosMap.size();
	if ( n > 0 ){
		delimiterPositions.resize(n);
		int i = 0;
		for ( iteratorLoop(delimPosMap) ){
			delimiterPositions[i].first() = it->first;
			delimiterPositions[i].second() = it->second;
			++i;
		}
	}
	
	delimiterIndex = 0;
	numberOfSections = n;
	placemark = 0;
	oddEven = false;
	delimitersAsSections = das;
}

OString OPString::nextSection (bool includeDelim){
	if ( delimiterIndex >= numberOfSections ){ return OString(); }
	
	if ( !delimitersAsSections){
	
		int p0 = placemark;
		int p1 = delimiterPositions[ delimiterIndex ].first() - 1;
		
		++delimiterIndex;
		placemark = p1 + 1 + whatDelimiterAtIndex(delimiterIndex - 1).length();
		
		if ( p1 < p0 ){ return nextSection(includeDelim); }
		return substring(p0,p1);
	
	}
	else{

		if ( oddEven ) { //if it's currently taking a delimiter token out
			int p = delimiterPositions[ delimiterIndex ].first();
			int l = whatDelimiterAtIndex( delimiterIndex ).length();
			++delimiterIndex;
			placemark = p + l;
			
			oddEven = !oddEven;
			if ( l > 0 ){
				return substring_l(p,l);
			}
			return nextSection(includeDelim);
		}
		else{
			int p = delimiterPositions[ delimiterIndex ].first() - 1;
			
			oddEven = !oddEven;
			if ( placemark <= p ){
				return substring(placemark,p);
			}
			else{
				return nextSection(includeDelim);
			}
		}
	}
	
	return OString();
}

void OPString::skipSection (){
	if ( !delimitersAsSections ){
		placemark = delimiterPositions[ delimiterIndex ].first() + whatDelimiterAtIndex(delimiterIndex).length();
		++delimiterIndex;
	}
	else{
		if ( oddEven ){
			int p = delimiterPositions[ delimiterIndex ].first();
			int l = whatDelimiterAtIndex( delimiterIndex ).length();
			++delimiterIndex;
			placemark = p + l;
			
			oddEven = !oddEven;
		}
		else{
			oddEven = !oddEven;
		}
	}
}

OString& OPString::whatDelimiterAtIndex (int di){
	return (delimiters[ delimiterPositions[di].second() ]);
}

OString& OPString::nextDelimiter (){
	return (delimiters[ delimiterPositions[ delimiterIndex ].second() ]);
}

OString& OPString::lastDelimiter (){
	return (delimiters[ delimiterPositions[ delimiterIndex - 1 ].second() ]);
}

void OPString::chunkify( const char *c, std::vector< OString >& rv ){

//	char c1 = c[0];
//	char c2 = c[1];
//	
//	std::stack < int > open;
//	int count = 0;
//	
//	for ( int i = 0; i < length(); ++i ){
//		if ( at(i) == c1 ) { open.push(i); }
//		else if ( at(i) == c2 ){
//			int open_index = open.top() + 1;
//			int close_index = i - 1;
//			rv.push_back( OPString( substring( open_index,close_index ) ) );
//			i -= close_index - open_index;
//			OString rep = "#";
//			rep += count;
//			rep += "#";
//			count++;
//			replace( open_index - 1,(close_index - open_index) + 3,rep );
//			open.pop();
//		}
//	}
//	
//	
}

}